/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * shvm.java
 *
 * Created on 03.03.2010, 19:08:14
 */

package vm;

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.io.File;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.BadLocationException;
import javax.swing.undo.UndoManager;

/**
 *
 * @author Peer
 */
public class shvm extends JPanel implements StackMachine, CaretListener, DocumentListener {

    vm parent;
    String name;
    shvmMachine machine;
    //Highlighter?
    UndoManager manager = new UndoManager();
    boolean CodeHasChanged = false;


    /** Creates new form shvm */
    public shvm(vm parent, String name) {
        this.parent = parent;
        this.name = name;
        initComponents();
        machine = new shvmMachine(name,codeArea,outputLabel);

        machine.registerView(this);
        //machine.registerStackViewer(stackModel);
        //machine.registerMemoryViewer(memoryModel);
        File saved = new File("machines/"+name+".shvm");
        if(saved.exists()) {
            machine.load();
            CodeHasChanged = false;
        }

    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        GridBagConstraints gridBagConstraints;

        inputLabel = new JLabel();
        jScrollPane1 = new JScrollPane();
        codeArea = new JTextArea();
        jScrollPane2 = new JScrollPane();
        jTable1 = new JTable();
        posLabel = new JLabel();
        totalLabel = new JLabel();
        cycleLabel = new JLabel();
        stackLabel = new JLabel();
        callStackLabel = new JLabel();
        outputLabel = new JLabel();
        pcLabel = new JLabel();
        jScrollPane5 = new JScrollPane();
        jTable3 = new JTable();

        setLayout(new GridBagLayout());

        inputLabel.setText("Input:");
        inputLabel.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 2));
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        gridBagConstraints.insets = new Insets(1, 2, 1, 2);
        add(inputLabel, gridBagConstraints);

        codeArea.setColumns(20);
        codeArea.setRows(5);
        codeArea.addCaretListener(this);
        codeArea.getDocument().addUndoableEditListener(manager);
        codeArea.getDocument().addDocumentListener(this);
        jScrollPane1.setViewportView(codeArea);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.weighty = 1.0;
        add(jScrollPane1, gridBagConstraints);

        jScrollPane2.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        jScrollPane2.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
        jScrollPane2.setFocusable(false);
        jScrollPane2.setRequestFocusEnabled(false);

        jTable1.setModel(new DefaultTableModel(
            new Object [][] {
                {"S0", new Integer(234324)},
                {"S1", new Integer(32432)},
                {"S2", new Integer(23432)},
                {"S3", new Integer(3)},
                {"S4", new Integer(234)},
                {"S5", new Integer(324)},
                {"S6", new Integer(324)},
                {"S7", new Integer(324)},
                {"S8", new Integer(23)},
                {"S9", new Integer(3435)}
            },
            new String [] {
                "#", "Value"
            }
        ) {
            Class[] types = new Class [] {
                String.class, Integer.class
            };
            boolean[] canEdit = new boolean [] {
                false, false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        jTable1.setFocusable(false);
        jTable1.setRequestFocusEnabled(false);
        jTable1.setRowSelectionAllowed(false);
        jScrollPane2.setViewportView(jTable1);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridheight = GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.ipadx = 75;
        gridBagConstraints.weighty = 1.0;
        add(jScrollPane2, gridBagConstraints);

        posLabel.setText("Position: 0:0");
        posLabel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED), BorderFactory.createEmptyBorder(1, 2, 1, 2)));
        posLabel.setName("Position: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        add(posLabel, gridBagConstraints);

        totalLabel.setText("Total: 0");
        totalLabel.setBorder(posLabel.getBorder());
        totalLabel.setName("Total: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        add(totalLabel, gridBagConstraints);

        cycleLabel.setText("Cycles: 0");
        cycleLabel.setBorder(posLabel.getBorder());
        cycleLabel.setName("Cycles: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 6;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        add(cycleLabel, gridBagConstraints);

        stackLabel.setText("Stack: 0");
        stackLabel.setBorder(posLabel.getBorder());
        stackLabel.setName("Stack: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        add(stackLabel, gridBagConstraints);

        callStackLabel.setText("Call Stack: 0");
        callStackLabel.setBorder(posLabel.getBorder());
        callStackLabel.setName("Call Stack: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        add(callStackLabel, gridBagConstraints);

        outputLabel.setText("Output: ");
        outputLabel.setBorder(BorderFactory.createCompoundBorder(new LineBorder(new Color(0, 0, 0), 2, true), BorderFactory.createEmptyBorder(1, 2, 1, 2)));
        outputLabel.setName("Output: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new Insets(1, 0, 1, 0);
        add(outputLabel, gridBagConstraints);

        pcLabel.setText("PC: 0");
        pcLabel.setBorder(posLabel.getBorder());
        pcLabel.setName("PC: "); // NOI18N
        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 5;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.anchor = GridBagConstraints.LINE_START;
        add(pcLabel, gridBagConstraints);

        jTable3.setModel(new DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Title 1", "Title 2", "Title 3", "Title 4"
            }
        ));
        jScrollPane5.setViewportView(jTable3);

        gridBagConstraints = new GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = GridBagConstraints.REMAINDER;
        gridBagConstraints.gridheight = GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = GridBagConstraints.BOTH;
        gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
        gridBagConstraints.weighty = 1.0;
        add(jScrollPane5, gridBagConstraints);
    }

    // Code for dispatching events from components to event handlers.

    public void caretUpdate(javax.swing.event.CaretEvent evt) {
        if (evt.getSource() == codeArea) {
            shvm.this.codeAreaCaretUpdate(evt);
        }
    }// </editor-fold>//GEN-END:initComponents

    // <editor-fold defaultstate="collapsed" desc="Event Handling">
    private void codeAreaCaretUpdate(CaretEvent evt) {//GEN-FIRST:event_codeAreaCaretUpdate
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    int y = 0;
                    int x = codeArea.getCaretPosition();
                    y = codeArea.getLineOfOffset(x);
                    x -= codeArea.getLineStartOffset(y);
                    posLabel.setText(posLabel.getName()+x+":"+y);
                } catch (BadLocationException ex) {
                    Logger.getLogger(hvm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }//GEN-LAST:event_codeAreaCaretUpdate

    public void insertUpdate(DocumentEvent e) {
        CodeHasChanged = true;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                updateUndoRedo();
            }
        });
    }

    public void removeUpdate(DocumentEvent e) {
        CodeHasChanged = true;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                updateUndoRedo();
            }
        });
    }

    public void changedUpdate(DocumentEvent e) {
        CodeHasChanged = true;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                updateUndoRedo();
            }
        });
    }

    public void cyclesUpdated(int cycles) {
        cycleLabel.setText(cycleLabel.getName()+Integer.toString(cycles));
    }

    public void stacksizeUpdated(int size) {
        stackLabel.setText(stackLabel.getName()+Integer.toString(size));
    }

    public void callstacksizeUpdated(int size) {
        callStackLabel.setText(callStackLabel.getName()+Integer.toString(size));
    }

    public void programcounterUpdated(int pos) {
        //pcLabel.setText(pcLabel.getName()+Integer.toString(pos));
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Menu Functions">
    public void copy() {
        codeArea.copy();
    }

    public void paste() {
        codeArea.paste();
    }

    public void cut() {
        codeArea.cut();
    }

    public void undo() {
        if (manager.canUndo()) {
            manager.undo();            
        }
    }

    public void redo() {
        if (manager.canRedo()) {
            manager.redo();            
        }
    }

    public void resetAndRun() {
        machine.reset();
        machine.run(codeArea.getDocument().getLength());
    }

    public void run() {
        machine.run(codeArea.getDocument().getLength());
    }

    public void runStep() {
        machine.runStep(true);
    }

    public void runToCursor() {
        machine.run(codeArea.getCaretPosition());
    }

    public void reset() {
        machine.reset();
    }    
    
    public void save() {
        machine.save();
        CodeHasChanged = false;
        parent.setChanged(CodeHasChanged);
    }    
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Costum Functions">
    public void setInput(String input) {
        machine.setInput(input);
        if(input.length()==0) {
            inputLabel.setText("Input: ");
            return;
        }
        Scanner scan = new Scanner(input).useDelimiter(",");
        StringBuilder ascii = new StringBuilder("");
        while(scan.hasNext()) {
            ascii.append((char)Integer.parseInt(scan.next()));
        }
        inputLabel.setText("Input: "+input+"  ("+ascii+")");
    }

    public boolean close() {
        //do anything, that needs to be done, before this machine can be destroyed like saving
        parent.broadcastStatus("Closing SHVM \""+name+"\"");
        if(CodeHasChanged) {
            int pick = JOptionPane.showConfirmDialog(this,
                                                     "Do you want to save SHVM \""+name+"\" before closing?",
                                                     "Closing",
                                                     JOptionPane.YES_NO_CANCEL_OPTION,
                                                     JOptionPane.QUESTION_MESSAGE);
            switch(pick) {
                case JOptionPane.YES_OPTION: save();
                case JOptionPane.NO_OPTION: setBusy(false, "SHVM \""+name+"\" closed"); return true;
                default: setBusy(false, "Closing aborted"); return false;
            }
        }
        parent.broadcastStatus("SHVM \""+name+"\" closed");
        return true;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
        machine.setName(name);
        CodeHasChanged = true;
    }

    public void updateUndoRedo() {
        parent.setUndo(manager.canUndo());
        parent.setRedo(manager.canRedo());
        parent.setChanged(CodeHasChanged);
    }

    public void setFocus() {
        codeArea.requestFocusInWindow();
    }

    public void broadcastStatus(String out) {
        if(parent!=null)
            parent.broadcastStatus(out);
    }

    public void setBusy(boolean startIt, String message) {
        if(parent!=null)
            parent.setBusy(startIt, message);
    }

    public void setBusy(boolean startIt) {
        if(parent!=null)
            parent.setBusy(startIt);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Variables declaration">
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private JLabel callStackLabel;
    private JTextArea codeArea;
    private JLabel cycleLabel;
    private JLabel inputLabel;
    private JScrollPane jScrollPane1;
    private JScrollPane jScrollPane2;
    private JScrollPane jScrollPane5;
    private JTable jTable1;
    private JTable jTable3;
    private JLabel outputLabel;
    private JLabel pcLabel;
    private JLabel posLabel;
    private JLabel stackLabel;
    private JLabel totalLabel;
    // End of variables declaration//GEN-END:variables
    // </editor-fold>
}
